package test

import (
	"crypto/md5"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"

	//"net/url"
	"strings"
	"sync/atomic"
	"time"

	"github.com/gogf/gf/encoding/gjson"
	"github.com/gogf/gf/util/gconv"
	"golang.org/x/net/proxy"
	"idoone.cc/idoonego/crypto/aes"
	"idoone.cc/idoonego/crypto/rsa"
)

type NodeConfigModel struct {
	Token     string
	timestamp uint64
	aeskey    []byte
	akey      string
	bkey      string
	abkey     string
}

var (
	nodeConfig = NodeConfigModel{
		Token:     "Kie9S",
		timestamp: 0,
		akey:      "a4f8aac9f5cea9a6",
		abkey:     "a4f8aac9f5cea9a6",
		aeskey:    []byte("a4f8aac9f5cea9a6"),
	}
)

type OpResult struct {
	Code string `json:"code"`
	Data string `json:"data"`
	Msg  string `json:"msg"`
}

type GenRSAModel struct {
	Akey   string `json:"akey"`
	Pubkey string `json:"pubkey"`
}

type BKeyModel struct {
	Bkey string `json:"bkey"`
}

func TestCustom() {
	str := "hello 中文"
	data, err := customRequest("http://127.0.0.1:8081/admin/api/genrsa", str)
	if err != nil {
		log.Printf("err:%v %s\n", err, data)
		return
	}
	log.Printf("%s\n", data)
	genrsa := GenRSAModel{}
	err = gjson.DecodeTo(data, &genrsa)
	if err != nil {
		log.Printf("err:%v %s\n", err, data)
		return
	}

	now := time.Now()
	salt := gconv.String(now.Unix())
	bkeyRaw := fmt.Sprintf("%x", md5.Sum([]byte(nodeConfig.Token+salt)))
	bkeyEnc, err := rsa.ECBPKCS1PaddingEncrypt([]byte(bkeyRaw), genrsa.Pubkey)
	if err != nil {
		log.Printf("err:%v %v\n", err, bkeyRaw)
		return
	}
	bkeyModel := BKeyModel{}
	bkeyModel.Bkey = base64.StdEncoding.EncodeToString(bkeyEnc)
	json, err := gjson.Encode(bkeyModel)
	if err != nil {
		log.Printf("err:%v %v\n", err, bkeyModel)
		return
	}
	data, err = customRequest("http://127.0.0.1:8081/admin/api/exchange", string(json))
	if err != nil {
		log.Printf("err:%v %s\n", err, data)
		return
	}
	log.Printf("%s\n", data)

	nodeConfig.abkey = fmt.Sprintf("%x", md5.Sum([]byte(genrsa.Akey+bkeyRaw)))[16:]
	nodeConfig.aeskey = []byte(fmt.Sprintf("%x", md5.Sum([]byte(genrsa.Akey+bkeyRaw+nodeConfig.akey)))[:16])
	data, err = customRequest("http://127.0.0.1:8081/admin/api/abaes", "Hello 中华")
	if err != nil {
		log.Printf("err:%v %s\n", err, data)
		return
	}
	log.Printf("%s\n", data)
}

func customRequest(URL, content string) (string, error) {
	contentEnc, err := aes.CBCPKCS5PaddingEncrypt([]byte(content), nodeConfig.aeskey)
	if err != nil {
		return "", err
	}
	contentb64 := base64.StdEncoding.EncodeToString(contentEnc)

	client := &http.Client{
		Transport: &http.Transport{
			Dial: func(netw, addr string) (net.Conn, error) {
				conn, err := net.DialTimeout(netw, addr, time.Second*10)
				if err != nil {
					return nil, err
				}
				conn.SetDeadline(time.Now().Add(time.Second * 300))
				return conn, nil
			},
			ResponseHeaderTimeout: time.Second * 300,
		},
	}
	var reader io.Reader = nil
	if contentb64 != "" {
		reader = strings.NewReader(contentb64)
	}
	req, err := http.NewRequest("POST", URL, reader)
	if err != nil {
		return "", err
	}
	if req.Body != nil {
		defer req.Body.Close()
	}

	headstamp := gconv.String(atomic.AddUint64(&nodeConfig.timestamp, 1))
	req.Header.Add("Content-Type", "text/plain")
	req.Header.Add("token", nodeConfig.Token)
	req.Header.Add("timestamp", headstamp)
	req.Header.Add("verify", fmt.Sprintf("%x", md5.Sum([]byte(nodeConfig.Token+nodeConfig.abkey+contentb64+nodeConfig.abkey+headstamp))))

	response, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}
	if response.StatusCode != 200 {
		return string(body), fmt.Errorf("HTTP %s", response.Status)
	}

	result := new(OpResult)
	err = gjson.DecodeTo(body, result)
	if err != nil {
		return string(body), err
	}
	if result.Code != "0" {
		return string(body), errors.New(result.Msg)
	}

	bodyEnc, err := base64.StdEncoding.DecodeString(result.Data)
	bodyDec, err := aes.CBCPKCS5PaddingDecrypt(bodyEnc, nodeConfig.aeskey)

	return string(bodyDec), nil
}

func TestHttpProxy() {
	proxyAddr := "110.90.221.67:57114"
	log.Printf("IsHttpProxyOk=%v\n", IsHttpProxyOk("http://39.100.74.6:8088/hello.txt", proxyAddr))

	data, err := HttpProxyGET("http://www.baidu.com/", proxyAddr)
	if err != nil {
		log.Println(err)
	} else {
		log.Println(string(data))
	}

	data, err = HttpProxyGET("https://www.baidu.com/", proxyAddr)
	if err != nil {
		log.Println(err)
	} else {
		log.Println(string(data))
	}
}

func HttpProxyGET(URL string, proxyAddr string) (body []byte, err error) {
	// proxy, err := url.Parse(proxyAddr)
	// if err != nil {
	// 	return nil, err
	// }
	// netTransport := &http.Transport{
	// 	Proxy: http.ProxyURL(proxy),
	// 	Dial: func(netw, addr string) (net.Conn, error) {
	// 		c, err := net.DialTimeout(netw, addr, time.Second*time.Duration(10))
	// 		if err != nil {
	// 			return nil, err
	// 		}
	// 		return c, nil
	// 	},
	// 	MaxIdleConnsPerHost:   50,                              // 每个host最大空闲连接
	// 	ResponseHeaderTimeout: time.Second * time.Duration(30), // 数据收发5秒超时
	// }
	dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
	if err != nil {
		log.Println("获取代理 error=", err.Error())
		return nil, err
	}
	netTransport := &http.Transport{}
	netTransport.Dial = dialer.Dial
	client := &http.Client{
		Timeout:   time.Second * 10,
		Transport: netTransport,
	}
	rsp, err := client.Get(URL)
	if err != nil {
		return nil, err
	}
	defer rsp.Body.Close()

	if rsp.StatusCode != http.StatusOK {
		err = fmt.Errorf("HTTP GET Code=%v, URI=%v, err=%v", rsp.StatusCode, URL, err)
	}

	return ioutil.ReadAll(rsp.Body)
}

func IsHttpProxyOk(URL string, proxyAddr string) bool {
	// proxy, err := url.Parse(proxyAddr)
	// if err != nil {
	// 	return false
	// }
	// netTransport := &http.Transport{
	// 	Proxy: http.ProxyURL(proxy),
	// 	Dial: func(netw, addr string) (net.Conn, error) {
	// 		c, err := net.DialTimeout(netw, addr, time.Second*time.Duration(10))
	// 		if err != nil {
	// 			return nil, err
	// 		}
	// 		return c, nil
	// 	},
	// 	MaxIdleConnsPerHost:   50,                              // 每个host最大空闲连接
	// 	ResponseHeaderTimeout: time.Second * time.Duration(30), // 数据收发5秒超时
	// }
	dialer, err := proxy.SOCKS5("tcp", proxyAddr, nil, proxy.Direct)
	if err != nil {
		log.Println("获取代理 error=", err.Error())
		return false
	}
	netTransport := &http.Transport{}
	netTransport.Dial = dialer.Dial
	client := &http.Client{
		Timeout:   time.Second * 10,
		Transport: netTransport,
	}
	rsp, err := client.Get(URL)
	if err != nil {
		log.Println(err)
		return false
	}
	defer rsp.Body.Close()
	return rsp.StatusCode == http.StatusOK
}
